//
// Created by yxyx on 2021-11-29.
//

#ifndef SFML_DEMO_BLUR_HPP
#define SFML_DEMO_BLUR_HPP


#include <SFML/Graphics/Image.hpp>
#include <cmath>
#include "matrix.hpp"
#include "utils.hpp"


int transInt(double x, int max, int min);

Matrix<double>* img2col(Matrix<double> *mat, int kernal_size){
    std::vector<double> col_data;
    for (int y = 0; y <= mat->height - kernal_size; ++y) {
        for (int x = 0; x <= mat->width - kernal_size; ++x) {
            auto tmp_sub_mat = mat->sub_mat(y, x, kernal_size, kernal_size);
            for (int i = 0; i < tmp_sub_mat->size(); ++i) {
                col_data.push_back(tmp_sub_mat->data[i]);
            }
            delete(tmp_sub_mat);
        }
    }

    int col_mat_w = kernal_size * kernal_size;
    int col_mat_h = col_data.size() / col_mat_w;
    auto col_mat = new Matrix<double>(col_mat_h, col_mat_w);
    for (int k = 0; k < col_data.size(); ++k) {
        col_mat->data[k] = col_data[k];
    }

    return col_mat;
}




#define PI 3.1415926
/**
 * 图像模糊算法
 * @return
 */

void gaussianfilter(sf::Image &src, int kernal_size, double sigmaX, double sigmaY){
    int img_w = src.getSize().x, img_h = src.getSize().y;
    Matrix<double> *kernel_ = new Matrix<double>(kernal_size, kernal_size);
    int h = (kernal_size - 1) / 2, w = (kernal_size - 1) / 2;
    double sum = 0.0;
    for (int i = 0; i < kernal_size; ++i)
    {
        for (int j = 0; j < kernal_size; ++j)
        {
            kernel_->set(i, j, exp(-(pow(i - h, 2) + pow(j - w, 2)) / (2 * sigmaX * sigmaY)));
            sum += kernel_->get(i, j);
        }
    }
    kernel_ = kernel_->operator/(sum);

    auto kernal = kernel_->reshape(1, -1);

    auto *r_mat = new Matrix<double>(img_h, img_w);
    auto *g_mat = new Matrix<double>(img_h, img_w);
    auto *b_mat = new Matrix<double>(img_h, img_w);
    for (int img_height = 0; img_height < img_h; ++img_height) {
        for (int img_width = 0; img_width < img_w; ++img_width) {
            r_mat->set(img_height, img_width, src.getPixel(img_width, img_height).r);
            g_mat->set(img_height, img_width, src.getPixel(img_width, img_height).g);
            b_mat->set(img_height, img_width, src.getPixel(img_width, img_height).b);
        }
    }

    auto p_r_mat = padding(r_mat, h, 0);
    auto p_g_mat = padding(g_mat, h, 0);
    auto p_b_mat = padding(b_mat, h, 0);

    auto col_r = img2col(p_r_mat, kernal_size);
    auto col_g = img2col(p_g_mat, kernal_size);
    auto col_b = img2col(p_b_mat, kernal_size);


    auto r = col_r->T();
    auto g = col_g->T();
    auto b = col_b->T();
    auto conv_r = kernal->Dot(r);
    auto conv_g = kernal->Dot(g);
    auto conv_b = kernal->Dot(b);

    auto result_r = conv_r->reshape(img_h, img_w);
    auto result_g = conv_g->reshape(img_h, img_w);
    auto result_b = conv_b->reshape(img_h, img_w);

    for (int img_height = 0; img_height < img_h; ++img_height) {
        for (int img_width = 0; img_width < img_w; ++img_width) {
            unsigned char r = static_cast<unsigned char>(result_r->get(img_height, img_width));
            unsigned char g = static_cast<unsigned char>(result_g->get(img_height, img_width));
            unsigned char b = static_cast<unsigned char>(result_b->get(img_height, img_width));
            sf::Color color(r, g, b, src.getPixel(img_width, img_height).a);
            src.setPixel(img_width, img_height, color);
        }
    }
}





void to_gray(sf::Image &src){
    int img_w = src.getSize().x, img_h = src.getSize().y;
    for (int img_height = 0; img_height < img_h; ++img_height) {
        for (int img_width = 0; img_width < img_w; ++img_width) {
            unsigned char gray = 0.299*src.getPixel(img_width, img_height).b +0.587* src.getPixel(img_width, img_height).r +  0.114 * src.getPixel(img_width, img_height).b;
            sf::Color color(gray, gray, gray, src.getPixel(img_width, img_height).a);
            src.setPixel(img_width, img_height, color);
        }
    }
}



/*二次线性插值算法*/
sf::Image * biInterpolation(sf::Image &src, int resizeW, int resizeH)
{
    int originH = src.getSize().y, originW = src.getSize().x;

    sf::Image * r_img = new sf::Image();
    r_img->create(resizeW, resizeH);

    // 按照行列计算缩放比例
    double rowRatio = ((double)originH / (double)resizeH);      // 行坐标比例
    double colRatio = ((double)originW / (double)resizeW);      // 列坐标比例
    int row;                                                    // 新图行坐标
    int col = 0;                                                // 新图旧坐标
    int maxR = originH - 1;                                     // 最大映射行坐标
    int maxC = originW - 1;                                     // 最大映射列坐标

    // 生成新图所有像素点ARGB值
    for(row = 0; row < resizeH; row++)
    {
        // 计算新图映射在原图中的行坐标
        double oriRow = row * rowRatio;
        // 原图行坐标整数部分
        double r = std::floor(oriRow);
        // 原图行坐标小数部分
        double rf = oriRow - r;

        for(col = 0; col < resizeW; col++)
        {
            // 计算新图映射在原图中的列坐标
            double oriCol = col * colRatio;
            // 原图列坐标整数部分
            double c = std::floor(oriCol);
            // 原图列坐标小数部分
            double cf = oriCol - c;

            // 二次线性插值算法中的四个计算因子
            double p1 = (1.0 - rf) * (1.0 - cf);
            double p2 = (1.0 - rf) * cf;
            double p3 = rf * (1.0 - cf);
            double p4 = rf * cf;

            // 分别对一个像素点的ARGB值进行计算

            auto val_a = (int)(
                    p1 * src.getPixel(transInt(c,maxC,0), transInt(r,maxR,0)).a
                    + p2 * src.getPixel(transInt(c+1,maxC,0), transInt(r,maxR,0)).a
                    + p3 * src.getPixel(transInt(c,maxC,0), transInt(r+1,maxR,0)).a
                    + p4 * src.getPixel(transInt(c+1,maxC,0), transInt(r+1,maxR,0)).a);

            auto val_r = (int)(
                    p1 * src.getPixel(transInt(c,maxC,0), transInt(r,maxR,0)).r
                    + p2 * src.getPixel(transInt(c+1,maxC,0),transInt(r,maxR,0)).r
                    + p3 * src.getPixel(transInt(c,maxC,0), transInt(r+1,maxR,0)).r
                    + p4 * src.getPixel( transInt(c+1,maxC,0), transInt(r+1,maxR,0)).r);

            auto val_g = (int)(
                    p1 * src.getPixel(transInt(c,maxC,0), transInt(r,maxR,0)).g
                    + p2 * src.getPixel(transInt(c+1,maxC,0), transInt(r,maxR,0)).g
                    + p3 * src.getPixel(transInt(c,maxC,0), transInt(r+1,maxR,0)).g
                    + p4 * src.getPixel(transInt(c+1,maxC,0), transInt(r+1,maxR,0)).g);

            auto val_b = (int)(
                    p1 * src.getPixel( transInt(c,maxC,0), transInt(r,maxR,0)).b
                    + p2 * src.getPixel(transInt(c+1,maxC,0), transInt(r,maxR,0)).b
                    + p3 * src.getPixel(transInt(c,maxC,0),transInt(r+1,maxR,0)).b
                    + p4 * src.getPixel(transInt(c+1,maxC,0), transInt(r+1,maxR,0)).b);


            r_img->setPixel(col, row, sf::Color(val_r, val_g, val_b, val_a));

        }// end for
    }// end for

    return r_img;
}// end method


/*方法一、二所需：将行列坐标整数部分转为int型，并且做边界检测*/
int transInt(double x, int max, int min)
{
    if(x > max)
    {
        return max;
    }
    else if (x < min)
    {
        return min;
    }
    else
    {
        return (int)x;
    }
}


#endif //SFML_DEMO_BLUR_HPP
